#include <algorithm>
#include <assert.h>
#include <iostream>

#include "foreach.hpp"
#include "gamemap.hpp"
#include "tile.hpp"
#include "string_utils.hpp"

namespace hex {

namespace {
bool string_empty(const std::string& str) { return str.empty(); }
}

gamemap::gamemap(const std::string& str)
{
	std::vector<std::string> lines = util::split(str, '\n');
	lines.erase(std::remove_if(lines.begin(), lines.end(), string_empty), lines.end());
	height_ = lines.size();
	if(height_ == 0) {
		std::cerr << "map is empty!\n";
		throw parse_error();
	}

	width_ = util::split(lines.front(), ',').size();
	if(width_ == 0) {
		std::cerr << "map is empty!\n";
		throw parse_error();
	}
	
	foreach(const std::string& line, lines) {
		std::vector<std::string> cells = util::split(line, ',');
		if(cells.size() != width_) {
			std::cerr << "row does not have the right number of cells!\n";
			throw parse_error();
		}

		foreach(const std::string& cell, cells) {
			const int index = tiles_.size();
			location loc(index%width_, index/width_);
			tiles_.push_back(tile_ptr(new tile(loc, cell)));
		}
	}

	assert(tiles_.size() == width_*height_);

	init_heightmap();
}

gamemap::gamemap(const std::vector<tile_ptr>& tiles, int width, int height)
  : width_(width), height_(height), tiles_(tiles)
{
}

void gamemap::add_river(river_ptr r)
{
	rivers_.push_back(r);
}

void gamemap::init_heightmap()
{
	heights_ = heightmap(2 + width_*3, 2 + height_*2);
	foreach(const_tile_ptr t, tiles_) {
		t->set_heightmap(heights_);
	}

/*
	foreach(const_river_ptr r, rivers_) {
		int previous_xpos = -100;
		int previous_ypos = -100;
		for(int n = 0; n != r->locs().size(); ++n) {
			const corner_location& loc = r->locs()[n];
			int xpos = 3*loc.hex.x() + (loc.left_side ? 0 : 4);
			int ypos = 2*loc.hex.y() + 1 + ((loc.hex.x()%2) == 0 ? 0 : 1);
			heights_.add(xpos, ypos, -0.5);

			//when drawing a horizontal line it will go across two places,
			//so we should fill in the place between
			if(previous_ypos == ypos && previous_xpos == xpos - 2) {
				heights_.set(xpos-1, ypos, -0.5);
			}

			if(previous_ypos == ypos && previous_xpos == xpos + 2) {
				heights_.set(xpos+1, ypos, -0.5);
			}
		}
	}
*/

	GLfloat variance = 0.5;
	for(int i = 0; i != 2; ++i) {
		heights_.subdivide(variance);
		variance *= 0.5;
	}

	for(int i = 0; i != 5; ++i) {
		heights_.erode(0.1, 0.5);
	}

	foreach(river_ptr r, rivers_) {
		r->do_erosion(heights_);
	}

	heights_.calculate_normals();
}

bool gamemap::is_loc_on_map(const location& loc) const
{
	if(loc.x() < 0 || loc.y() < 0 || loc.x() >= width_ || loc.y() >= height_) {
		return false;
	}

	return true;
}

tile_ptr gamemap::get_tile(const location& loc)
{
	if(!is_loc_on_map(loc)) {
		return tile_ptr();
	}

	const int index = loc.y()*width_ + loc.x();
	assert(index >= 0 && index < tiles_.size());
	return tiles_[index];
}

const_tile_ptr gamemap::get_tile(const location& loc) const
{
	if(!is_loc_on_map(loc)) {
		return const_tile_ptr();
	}

	const int index = loc.y()*width_ + loc.x();
	assert(index >= 0 && index < tiles_.size());
	return tiles_[index];
}

}
